package com.hy.servlet;

import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@WebServlet(value = "/", loadOnStartup = -1)
@MultipartConfig
public class DispatcherServlet extends HttpServlet {
    //创建一个bean容器
    private Map<String, Object> beanMap = new ConcurrentHashMap<>();
    private ObjectMapper objectMapper = new ObjectMapper();

    public DispatcherServlet() {
        //创建一个bean资源
        InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("applicationContext.xml");

        //1，通过工厂模式，创建documentBuilderFactory工厂对象
        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();

        try {
            //2，通过工厂对象，创建DocumentBuilder对象
            DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
            //得到Document对象
            Document document = documentBuilder.parse(inputStream);//相当于js中的document

            NodeList nodeList = document.getElementsByTagName("bean");

            for (int i = 0; i < nodeList.getLength(); i++) {
                Node node = nodeList.item(i);

                //Node类中没有提供更好的方法用来获取id,class属性
                Element element = (Element) node;

                String id = element.getAttribute("id");
                String className = element.getAttribute("class");

                if (beanMap.containsKey(id))
                    continue;
                //通过反射机制来创建对象
                Class clazz = Class.forName(className);

                Object xxxController = clazz.newInstance();

                beanMap.put(id, xxxController);
            }

        } catch (ParserConfigurationException | SAXException | IOException | ClassNotFoundException | InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {


        String requestURI = req.getRequestURI();

        int length1 = req.getContextPath().length();
         requestURI = requestURI.substring(length1+1);
//        int lastIndexOf = requestURI.lastIndexOf("/");
//        requestURI = requestURI.substring(lastIndexOf + 1);

        Object xxxController = beanMap.get(requestURI);

        if (xxxController == null)
            return;

        String ac = req.getParameter("ac");
        if (StrUtil.isBlank(ac))
            return;
        Method[] methods = xxxController.getClass().getMethods();
        for (Method method : methods) {
            if (ac.equals(method.getName())) {
                try {

                    Parameter[] parameters = method.getParameters();
                    int length = parameters.length;
                    Object[] parameterObjs = new Object[length];

                    for (int i = 0, len = parameters.length; i < len; i++) {
                        if (parameters[i].getType() == String.class) {
                            parameterObjs[i] = req.getParameter(parameters[i].getName());
                        } else if (parameters[i].getType() == Integer.class || "int".equals(parameters[i].getType().getName())) {
                            parameterObjs[i] = Integer.parseInt(req.getParameter(parameters[i].getName()));
                        } else if (parameters[i].getType() == Long.class || "long".equals(parameters[i].getType().getName())) {
                            parameterObjs[i] = Long.parseLong(req.getParameter(parameters[i].getName()));
                        } else if (parameters[i].getType() == HttpServletRequest.class) {
                            parameterObjs[i] = req;
                        } else if (parameters[i].getType() == HttpServletResponse.class) {
                            parameterObjs[i] = resp;
                        } else if (parameters[i].getType() == javax.servlet.http.HttpSession.class) {
                            parameterObjs[i] = req.getSession();
                        } else if (parameters[i].getType() == Boolean.class || "boolean".equals(parameters[i].getType().getName())) {
                            parameterObjs[i] = Boolean.parseBoolean(req.getParameter(parameters[i].getName()));
                        } else if (parameters[i].getType() == Double.class || "double".equals(parameters[i].getType().getName())) {
                            parameterObjs[i] = Double.parseDouble(req.getParameter(parameters[i].getName()));
                        } else if (parameters[i].getType() == Float.class || "float".equals(parameters[i].getType().getName())) {
                            parameterObjs[i] = Float.parseFloat(req.getParameter(parameters[i].getName()));
                        }
                    }

                    Object returnObj = method.invoke(xxxController, parameterObjs);

                    if (returnObj != null && returnObj.getClass() == String.class) {
                        String returnStr = (String) returnObj;
                        if (returnStr.startsWith("redirect:")) {
                            String redirectStr = returnStr.substring("redirect:".length());
                            resp.sendRedirect(req.getContextPath() + redirectStr);
                            return;
                        } else if (returnStr.startsWith("forward:")) {
                            String forwardStr = returnStr.substring("forward:".length());
                            req.getRequestDispatcher(forwardStr).forward(req, resp);
                            return;
                        }
                    } else if (returnObj != null && returnObj instanceof Object) {
                        PrintWriter out = resp.getWriter();
                        String jsonStr = objectMapper.writeValueAsString(returnObj);
                        out.write(jsonStr);
                        out.flush();
                        out.close();
                    }

                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
